function createContextMenuHandle() {
  /**
 * @typedef menuMap
 * @property {string} name 中文字段
 * @property {string} id 唯一标识
 * @property {function} onClick 对应的点击事件
 * @property {string} icon 图标
 * @property {Boolean | function} show 是否显示菜单
 * @property {newPanel | expand} panelType 类型，分为<newPanel: 使用新面板创建子菜单><expand展开作为子菜单>
 * @property {menuMap[]} children 子菜单
*/
  /** 生成随机数 */
  function getRandomId(maxLength = 8) {
    const $charts = 'ABCDEFGHIJKLMNOPQRSTUVWXWZabcdefghijklmnopqrstuvwxyz1234567890'
    let str = ''
    for (let i = 0; i < maxLength; i++) {
      str += $charts.charAt(Math.floor(Math.random() * $charts.length))
    }
    return str
  }
  /** 外部点击 */
  function clickOutside(win) {
    win.addEventListener('click', listenerClick, false);
    function listenerClick(e) {
      let flag = false;
      for (let index = 0; index < self.length; index++) {
        if ($.contains(self[index], e.target)) {
          flag = true;
          return;
        }
      }
      if (!flag) {
        fun(e.target);
      }
    }
  }

  /**
  * @property
  * @param {Element} parent 需要被插入的元素
  * @param { {x: number, y: number} } position 鼠标位置
  * @param {menuMap} menuMap 创建的菜单数组
  * @param {string} key 一个标识，没其他作用
  */
  function ContextMenu(parent, position, menuMap, key) {
    if (menuMap.length === 0 || menuMap.every(i => i.show === false)) return
    this.key = key
    this.parent = parent
    this.menuMap = this.addRandomId(menuMap) // 防止传入的数据id为空
    this.allPanels = []
    this.className = `${key}Contextmenu`
    this.clickOutsideListener = () => { }
    this.destroy()
    this.createContextMenu(this.menuMap, position)
  }

  /** 递归插入id，防止传入的数据id为空, level: 标识一下层级 */
  ContextMenu.prototype.addRandomId = function (list, level = 1) {
    list.forEach(i => {
      i.id = i.id || getRandomId()
      i.level = level
      if (Array.isArray(i.children)) {
        i.children = this.addRandomId(i.children, level + 1)
      }
    })
    return list
  }

  ContextMenu.prototype.createContextMenu = function (menuMap, position, parentMenu = {}) {
    if (this.allPanels.some(i => i.id === parentMenu.id)) return // 防止重复菜单创建

    if (this.allPanels.length === 2) { // 目前只考虑到两层
      this.allPanels[1].remove()
      this.allPanels.splice(1, 1)
    }

    const container = this.createPanel(position.x, position.y, parentMenu)
    menuMap.forEach(menu => {
      if (menu.show === false) return
      if (typeof menu.show === 'function' && menu.show() === false) return
      const menuElement = this.createMenu(menu)
      container.appendChild(menuElement)

      menuElement.addEventListener('mouseover', () => {
        if (this.allPanels.length === 2 && menu.level === 1) { // 目前只考虑到两层
          this.allPanels[1].remove()
          this.allPanels.splice(1, 1)
        }
      })
      // 创建子菜单
      if (Array.isArray(menu.children) && menu.children.length) {
        if (menu.panelType === 'expand') { // 向下展开菜单
          this.expandMenuHandle(menuElement, menu.children, container)
        }

        if (menu.panelType === 'newPanel') { // 展开新菜单
          menuElement.addEventListener('mouseover', () => {
            const x = menuElement.offsetParent.style.left.replace('px', '')
            const y = menuElement.offsetParent.style.top.replace('px', '')
            const w = menuElement.offsetParent.getBoundingClientRect().width
            this.createContextMenu(menu.children, { x: w + +x - 10, y }, menu)
          })
        }
      }
    })
    this.parent.appendChild(container)

    // // 下方边缘化处理
    // const bound = container.getBoundingClientRect();
    // const bodyBound = document.body.getBoundingClientRect();
    // if (bound.bottom > bodyBound.bottom) {
    //   container.style.top = `${bodyBound.height - bound.height}px`;
    // }
  }

  /** 渲染展开菜单，给展开注册事件等等 */
  ContextMenu.prototype.expandMenuHandle = function (ele, children, container) {
    const random = getRandomId()
    ele.addEventListener('click', () => {
      const emptyEle = $(`#${random}`);
      if (emptyEle[0].style.display === 'none') {
        ele.children[1].src = 'static/img/jsmind/arrow-bottom.svg'
        ele.children[1].style.width = '16px'
        emptyEle.show()
      } else {
        ele.children[1].src = 'static/img/jsmind/arrow-right.svg'
        ele.children[1].style.width = '16px'
        emptyEle.hide()
      }
    })
    const empty = document.createElement('div');
    empty.id = random; // 包裹一个空元素，让整体实现收缩
    children.forEach(menu => {
      if (menu.show === false) return
      if (typeof menu.show === 'function' && menu.show() === false) return
      const menuElement = this.createMenu(menu)
      empty.appendChild(menuElement)
    })
    container.appendChild(empty)
  }
  /**
  * 点面板的外部，销毁
  */
  /** 创建一个右键菜单面板 */
  ContextMenu.prototype.createPanel = function (x, y, parentMenu) {
    const container = document.createElement('div');
    container.id = parentMenu.id
    container.className = 'contextMenu-panel ' + this.className
    container.style.cssText = `position: absolute; top: ${y}px; left: ${x}px; min-width: 130px; background-color: #fff; border: 1px solid #eee; border-radius: 5px; z-index: 19999;padding: 10px 0; max-height: 300px; overflow-y: auto; box-shadow: rgba(60, 64, 67, 0.15) 0px 2px 6px 2px; color: #666666;`

    this.allPanels.push(container)

    // 点击外部，隐藏
    // this.clickOutside(window, mxUtils.bind(this, function (target) {
    //   let flag = true;
    //   const self = $(`${this.className}-expand`) // 展开菜单点击时是展开，不需要默认隐藏
    //   for (let index = 0; index < self.length; index++) {
    //     if ($.contains(self[index], target)) {
    //       flag = false;
    //     }
    //   }
    //   if (!flag) return
    //   this.destroy()
    // }))

    return container
  }

  /** 创建一个子菜单 */
  ContextMenu.prototype.createMenu = function (menu) {
    const item = document.createElement('div');
    item.style.cssText = `padding: 0 14px; height: 30px; line-height: 30px; font-size: 14px; color: rgb(102, 102, 102); cursor: pointer; max-width: 150px; overflow-x: hidden; white-space: nowrap; text-overflow: ellipsis;display: flex;align-items: center;`

    if (menu.icon) {
      const prefixIcon = document.createElement('img')
      prefixIcon.src = menu.icon;
      prefixIcon.style = 'height:16px;margin-right:10px;position:relative;top:2px;';
      item.appendChild(prefixIcon);
    }

    const nameSpan = document.createElement('div');
    nameSpan.innerText = menu.name;
    nameSpan.title = menu.name;
    nameSpan.style.cssText = `overflow: hidden;text-overflow: ellipsis;white-space: nowrap;width: calc(100% - 28px)`
    item.appendChild(nameSpan);
    if (typeof menu.onClick === 'function') {
      item.addEventListener('click', () => {
        this.destroy()
        menu.onClick()
      })
    }

    if (Array.isArray(menu.children) && menu.children.length) {
      const expandIcon = document.createElement('img');
      expandIcon.style.width = '16px'
      if (menu.panelType === 'expand') { // 向下展开菜单
        const expandIcon = document.createElement('img');
        expandIcon.src = 'img/icon/arrow-bottom.svg'
        item.className = `${this.className}-expand`
        item.style.fontWeight = 700
        item.style.fontSize = '15px'
        item.style.justifyContent = 'space-between'
        item.appendChild(expandIcon);
      }

      if (menu.panelType === 'newPanel') { // 展开新菜单
        expandIcon.src = 'img/icon/arrow-right.svg'
        item.appendChild(expandIcon);
      }

    }

    if (menu.appendSlot) {
      nameSpan.style.paddingRight = '10px'

      const append = document.createElement('div')
      append.innerText = menu.appendSlot
      append.style = 'color: #2288ff';
      item.style.justifyContent = 'space-between'
      item.style.padding = '0 14px'
      item.appendChild(append);

    }
    $(item).hover(() => {
      $(item).css('background-color', '#eee')
    }, () => {
      $(item).css('background-color', '#fff')
    })

    return item
  }

  /** 外部点击 */
  ContextMenu.prototype.clickOutside = function (win, fun) {
    win.removeEventListener('click', this.clickOutsideListener)

    this.clickOutsideListener = function (e) {
      const self = $('.contextMenu-panel')
      let flag = false;
      for (let index = 0; index < self.length; index++) {
        if ($.contains(self[index], e.target)) {
          flag = true;
          return;
        }
      }
      if (!flag) {
        fun(e.target);
      }
    }
    win.addEventListener('click', this.clickOutsideListener, false);
  }
  /** 销毁dom */
  ContextMenu.prototype.destroy = function () {
    if (!this.allPanels || !this.allPanels.length) return
    this.allPanels.forEach(i => i.remove())
    this.allPanels = []
  }

  return ContextMenu
}
// 引导页点击，悬浮的一些效果
function GuideEvent(editorUi) {
  this.editorUi = editorUi
  this.currentState = null
  this.graph = editorUi.editor.graph
  this.clichElemet = null
  this.hoverElemet = null
  this.cellInfoMap = new Map() // 实例的list数据，用于点击展示
  this.init()
}

GuideEvent.prototype.init = function () {
  const graphEvent = this
  this.graph.addMouseListener({
    mouseDown: mxUtils.bind(this, function (sender, me) {
      this.clichElemetDestroy()
      this.hoverElemetDestroy()
      this.createClickElementHandle()
    }),
    mouseUp: mxUtils.bind(this, function (sender, me) { }),
    mouseMove: mxUtils.bind(this, function (sender, me) {
      if (this.currentState != null && (me.getState() == this.currentState ||
        me.getState() == null)) {
        var tol = iconTolerance;
        var tmp = new mxRectangle(me.getGraphX() - tol,
          me.getGraphY() - tol, 2 * tol, 2 * tol);

        if (mxUtils.intersects(tmp, this.currentState)) {
          return;
        }
      }

      var tmp = this.graph.view.getState(me.getCell());

      if (tmp && tmp.cell && tmp.cell.id === '3') {
        tem = null
      }

      // Ignores everything but vertices
      if ((tmp != null && !graphEvent.graph.getModel().isVertex(tmp.cell))) {
        tmp = null;
      }

      if (tmp != this.currentState) {
        if (this.currentState != null) {
          this.hoverElemetDestroy()
        }

        this.currentState = tmp;
        if (this.currentState != null) {
          this.hoverElemetDestroy()
          this.createHoverElementHandle()
        }
      }
    }),
    dragEnter: mxUtils.bind(this, function (sender, me) {
      this.createHoverElementHandle()
    }),
    dragLeave: mxUtils.bind(this, function (sender, me) {
      this.hoverElemetDestroy()
    }),
  })
}

GuideEvent.prototype.createClickElementHandle = async function () {
  if (!this.currentState || !window.isGuideModel) return
  const cell = this.currentState.cell;
  const cellValue = cell.getValue();
  if (
    !(cellValue && typeof cellValue === 'object' && cellValue.getAttribute('display') === 'LABEL_RECTANGLE_GUIDE')
  )
    return;

  var cellInfo = this.cellInfoMap.get(cell.id)
  if (!cellInfo) return;
  if (this.contextMenu) {
    this.contextMenu.destroy()
    this.contextMenu = null
  }
  const menuMap = (cellInfo.data || []).map(item => {
    return {
      name: item.name,
      show: true,
      appendSlot: '进入',
      onClick: () => {
        this.clichElemetDestroy()
        this.hoverElemetDestroy()
        window.sidebarStructure.selectedNodeId = item.id
        window.sidebarStructure.setNodeCheckAndExpandById(item.id);
      }
    }
  })

  let typeList = [];
  await window.$axios
    .post(window.top.g.ApiUrl2 + '/sysmlDefineNode/getSYSMLModelTypes?type=design_item_guide' + '&metaId=' + cellValue.getAttribute('metaId'))
    .then(res => {
      typeList = res.data || [];
    });

  const createMenu = typeList.map(i => {
    return {
      ...i,
      panelType: 'newPanel',
      children: i.children.map(t => {
        return {
          ...t,
          icon: 'img/' + t.icon,
          onClick: () => {
            // dododo
            console.log(cellInfo);
            const treeNode = {
              metaName: undefined
            }
            window.drawioBus.$emit('dataTreeDialog', {
              type: t.name, treeNode, id: t.id, callback: () => {
                window.$axios
                  .post(window.top.g.ApiUrl2 + '/metaModel/openGraphPageOfTask', {
                    frameworkCode: window.frameworkCode,
                    designTaskId: window.parent.getProjectStore(window.parent.$constants.TASK_ID),
                  })
                  .then(res => {
                    const addedCellId = cellValue.getAttribute('metaId')
                    const info = res.data.otherInformation[addedCellId]
                    this.cellInfoMap.set(cell.id, info)
                  });
              }
            })
          }
        }
      })
    }
  })

  // if(!window.drawioStore.getState('designReadOnly')){
  //   menuMap.unshift(...createMenu)
  // }

  this.contextMenu = new (createContextMenuHandle())(this.graph.container, {
    x: this.currentState.x, y: this.currentState.y + this.currentState.height
  }, menuMap, 'guide')
}

GuideEvent.prototype.createHoverElementHandle = function () {
  if (!this.currentState || !window.isGuideModel) return

  const cell = this.currentState.cell;
  const cellValue = cell.getValue();
  if (
    !(cellValue && typeof cellValue === 'object' && cellValue.getAttribute('display') === 'LABEL_RECTANGLE_GUIDE')
  )
    return;
  this.hoverElemet = document.createElement('div');
  this.hoverElemet.className = 'geToolbarContainer geSidebarContainer geSidebar connectSelect';
  this.hoverElemet.style.cssText =
    'position:absolute;left:' +
    (this.currentState.x + this.currentState.width) +
    'px;top:' +
    (this.currentState.y) +
    'px;padding: 0px;text-align:left;border:unset; box-shadow: rgb(209 209 209) 0px 0px 3px 1px;font-size:12px;width: 300px;border-radius: 5px;';

  const cellInfo = this.cellInfoMap.get(cell.id)
  if (!cellInfo) return
  const header = document.createElement('div')
  header.style.width = '300px'
  header.style.height = '30px'
  header.style.lineHeight = '30px'
  header.style.border = '1px solid #eee'
  header.style.fontSize = '14px'
  header.style.position = 'relative'
  header.style.padding = '0 10px'
  header.innerHTML = cell.value instanceof Object ? cell.value.getAttribute('name') : ''

  const tooltip = document.createElement('div')
  tooltip.style.position = 'absolute'
  tooltip.style.right = '30px'
  tooltip.style.top = '0px'
  tooltip.style.color = '#409eff'
  tooltip.style.cursor = 'pointer'
  tooltip.style.fontSize = '12px'
  tooltip.innerHTML = '模型示例'
  tooltip.addEventListener('click', () => {
    window.drawioBus && window.drawioBus.$emit('modelExample', cellInfo.nameEn)
  })
  header.append(tooltip)

  const content = document.createElement('div')
  content.innerText = cellInfo.label
  content.style.width = '300px'
  content.style.height = '150px'
  content.style.overflowY = 'scroll'
  content.style.padding = '5px 10px'
  content.style.textIndent = '2em'

  this.hoverElemet.appendChild(header)
  this.hoverElemet.appendChild(content)

  if (cellInfo.label) {
    this.graph.container.appendChild(this.hoverElemet);
    const bound = this.hoverElemet.getBoundingClientRect()
    console.log(bound);
  }
}


GuideEvent.prototype.setCellStyle = function (data_xml) {
  // state:状态（"0":未开始， "1"进行中，"2"已超时）
  // type :是否必选
  const strokeColors = ['#000', '#22aa33', '#2288ff'] // 元素的边框颜色
  const bgColors = ['#fff', '#f0f9e8', '#ecf5ff'] // 元素的填充颜色
  const iconColors = [['#f5f5f5', '#000'], ['#22aa33', '#fff'], ['#2288ff', '#fff']] // 数字小标的颜色

  const xmlZap = window.myEditor.editor.graph.zapGremlins(mxUtils.trim(data_xml));
  const xmlHtml = mxUtils.parseXml(xmlZap);

  // 左上角比做的红色三角
  const createIcon = (parentId) => {
    return `<mxCell id="${this.graph.model.createId()}" value="" style="html=1;shape=mxgraph.basic.acute_triangle;dx=0;fillColor=#e51400;direction=south;strokeColor=none;" parent="${parentId}" vertex="1"><mxGeometry x="2" y="2" width="10" height="10" as="geometry"/></mxCell>`
  }

  // 右上角的数理统计
  const createCount = (parentId, width, count, state) => {
    return `<mxCell id="${this.graph.model.createId()}" value="${count}" style="ellipse;rounded=1;strokeColor=none;whiteSpace=wrap;html=1;spacingTop=3;${mxConstants.STYLE_FONTCOLOR}=${iconColors[state][1]};${mxConstants.STYLE_FILLCOLOR}=${iconColors[state][0]};" parent="${parentId}" vertex="1"><mxGeometry x="${width - 10}" y="-10" width="20" height="20" as="geometry"/></mxCell>`
  }


  const iconList = []
  const countList = []
  const guideInfo = window.guideInfo
  for (let i = 0; i < xmlHtml.documentElement.children[0].children.length; i++) {
    let node = xmlHtml.documentElement.children[0].children[i]
    if (typeof node === 'object') {
      const metaId = node.getAttribute('metaId')
      const name = node.getAttribute('name')

      if (guideInfo && guideInfo.hasOwnProperty(metaId)) {

        if (node.children.length && typeof node.children[0] === 'object') {
          const info = guideInfo[metaId]
          // let style = node.children[0].getAttribute('style')
          // style += `${mxConstants.STYLE_STROKECOLOR}=${strokeColors[info.state]};${mxConstants.STYLE_FONTCOLOR}=${strokeColors[info.state]};${mxConstants.STYLE_FILLCOLOR}=${bgColors[info.state]};gradientColor=none;`
          // node.children[0].setAttribute('style', style)

          if (info.type === 'true' || info.type === true) {
            iconList.push(createIcon(node.getAttribute('id')))
          }

          countList.push(createCount(node.getAttribute('id'), node.children[0].children[0].getAttribute('width'), info.number || 0, info.state))

          this.cellInfoMap.set(node.getAttribute('id'), info)
        }

      }
    }
  }

  iconList.forEach(icon => {
    const node = mxUtils.parseXml(icon).documentElement
    xmlHtml.documentElement.children[0].appendChild(node)
  })
  countList.forEach(icon => {
    const node = mxUtils.parseXml(icon).documentElement
    xmlHtml.documentElement.children[0].appendChild(node)
  })

  return xmlHtml
}

GuideEvent.prototype.clichElemetDestroy = function () {
  if (this.contextMenu) {
    this.contextMenu.destroy()
    this.contextMenu = null
  }
  return
  if (this.clichElemet != null) {
    this.clichElemet.parentNode && this.clichElemet.parentNode.removeChild(this.clichElemet);
    this.clichElemet = null;
  }
}


GuideEvent.prototype.hoverElemetDestroy = function () {
  if (this.hoverElemet != null) {
    this.hoverElemet.parentNode && this.hoverElemet.parentNode.removeChild(this.hoverElemet);
    this.hoverElemet = null;
  }
}
